package sets

// 源自https://github.com/Workiva/go-datastructures/blob/master/set/dict.go

type Set[V comparable] struct {
	items     map[V]struct{}
	flattened []V
}

// New is the constructor for sets. It will pull from a reuseable memory pool if it can.
// Takes a list of items to initialize the set with.
func New[V comparable](items ...V) *Set[V] {
	lenItems := len(items)

	set := &Set[V]{
		items: make(map[V]struct{}, lenItems),
	}

	for _, item := range items {
		set.items[item] = struct{}{}
	}

	if lenItems > 0 {
		set.flattened = nil
	}

	return set
}

// Add will add the provided items to the set.
func (set *Set[V]) Add(items ...V) {
	set.flattened = nil
	for _, item := range items {
		set.items[item] = struct{}{}
	}
}

// Remove will remove the given items from the set.
func (set *Set[V]) Remove(items ...V) {
	set.flattened = nil
	for _, item := range items {
		delete(set.items, item)
	}
}

// Exists returns a bool indicating if the given item exists in the set.
func (set *Set[V]) Exists(item V) bool {
	_, ok := set.items[item]
	return ok
}

// All returns a bool indicating if all of the supplied items exist in the set.
func (set *Set[V]) All(items ...V) bool {
	for _, item := range items {
		if _, ok := set.items[item]; !ok {
			return false
		}
	}

	return true
}

// Flatten will return a list of the items in the set.
func (set *Set[V]) Flatten() []V {
	if set.flattened != nil {
		return set.flattened
	}

	set.flattened = make([]V, 0, len(set.items))
	for item := range set.items {
		set.flattened = append(set.flattened, item)
	}
	return set.flattened
}

// Len returns the number of items in the set.
func (set *Set[V]) Len() int {
	return len(set.items)
}

// Clear will remove all items from the set.
func (set *Set[V]) Clear() {
	set.items = map[V]struct{}{}
	set.flattened = nil
}

func (set *Set[V]) Each(fn func(item V) (continued bool)) {
	for item := range set.items {
		if !fn(item) {
			break
		}
	}
}
